#include "Direct3DFramework.h"

Direct3DFramework::Direct3DFramework()
{
	//null all pointer attributes
	m_window = 0;
	m_object = 0;
	m_device = 0;
	
	//null all camera attributes
	m_camAspect = 0;
	m_camNear = 0;
	m_camFar = 0;
	m_camFov = 0;

	m_surfBack = 0;
	m_cEffect = 0;
	for(int i = 0; i < 6; i++)
	{
		m_cTexture2D[i] = 0;
		m_cTexture3D[i] = 0;
	}

	//empty all value attributes
	ZeroMemory(&m_present,sizeof(m_present));
	ZeroMemory(&m_display,sizeof(m_display));
	ZeroMemory(&m_capability,sizeof(m_capability));
	ZeroMemory(&m_cMaterial,sizeof(m_cMaterial));
	ZeroMemory(&m_camPos,sizeof(m_camPos));
	ZeroMemory(&m_camLook,sizeof(m_camLook));
	ZeroMemory(&m_camUp,sizeof(m_camUp));
	ZeroMemory(&m_camRight,sizeof(m_camRight));
}

Direct3DFramework::~Direct3DFramework()
{
	//implicitly release
	release();
}

int Direct3DFramework::init(WindowAppFramework* a_window, bool a_vsync)
{
	//obtain handle to window object
	m_window = a_window;

	//zero out graphics information structs
	ZeroMemory(&m_present,sizeof(m_present));
	ZeroMemory(&m_display,sizeof(m_display));
	ZeroMemory(&m_capability,sizeof(m_capability));

	//obtain the direct3d object interface
	m_object = Direct3DCreate9(D3D_SDK_VERSION);

	//obtain display mode info
	m_object->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&m_display);

	//obtain device capability info
	m_object->GetDeviceCaps(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,&m_capability);

	//set the present parameter struct
	m_present.hDeviceWindow					= m_window->m_hWnd;
	m_present.Windowed						= m_window->m_windowed;
	m_present.SwapEffect					= D3DSWAPEFFECT_DISCARD;
	m_present.BackBufferCount				= 1;
	m_present.BackBufferFormat				= D3DFMT_X8R8G8B8;
	m_present.BackBufferWidth				= (UINT)m_window->m_windowWidth;
	m_present.BackBufferHeight				= (UINT)m_window->m_windowHeight;
	m_present.EnableAutoDepthStencil		= true;
	m_present.AutoDepthStencilFormat		= D3DFMT_D16;
	m_present.MultiSampleType				= D3DMULTISAMPLE_NONE;
	m_present.MultiSampleQuality			= 0;
	m_present.FullScreen_RefreshRateInHz	= D3DPRESENT_RATE_DEFAULT;
	m_present.PresentationInterval			= (a_vsync)?D3DPRESENT_INTERVAL_DEFAULT:D3DPRESENT_INTERVAL_IMMEDIATE;
	m_present.Flags							= 0;

	//check gpu support for hardware vertex processing (faster)
	//else use cpu software vertex processing (slower)
	DWORD deviceFlag = 0;
	if(m_capability.DevCaps & D3DCREATE_HARDWARE_VERTEXPROCESSING)
		deviceFlag |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
	else
		deviceFlag |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;

	//if hardware processing is supported
	//check if a pure device can be created
	if((m_capability.DevCaps & D3DDEVCAPS_PUREDEVICE)
		&& (deviceFlag & D3DCREATE_HARDWARE_VERTEXPROCESSING))
		deviceFlag |= D3DCREATE_PUREDEVICE;

	//create direct3d device interface
	m_object->CreateDevice(
		D3DADAPTER_DEFAULT,
		D3DDEVTYPE_HAL,
		m_window->m_hWnd,
		deviceFlag,
		&m_present,
		&m_device);

	//create line interface
	D3DXCreateLine(m_device, &m_line);

	//create font interface
	D3DXCreateFontA(m_device,
		16,0,FW_NORMAL,0,false,
		DEFAULT_CHARSET,
		OUT_DEVICE_PRECIS,
		PROOF_QUALITY,
		FIXED_PITCH | FF_DONTCARE,
		TEXT("Consolas"),
		&m_font);

	//create format for vertex declaration
	D3DVERTEXELEMENT9 vertFormat[] =
	{
		//position
		{0,0,D3DDECLTYPE_FLOAT3,D3DDECLMETHOD_DEFAULT,D3DDECLUSAGE_POSITION,0},

		//normal
		{0,12,D3DDECLTYPE_FLOAT3,D3DDECLMETHOD_DEFAULT,D3DDECLUSAGE_NORMAL,0},

		//uv
		{0,24,D3DDECLTYPE_FLOAT2,D3DDECLMETHOD_DEFAULT,D3DDECLUSAGE_TEXCOORD,0},

		//tangent
		{0,36,D3DDECLTYPE_FLOAT3,D3DDECLMETHOD_DEFAULT,D3DDECLUSAGE_TANGENT,0},

		//binormal
		{0,48,D3DDECLTYPE_FLOAT3,D3DDECLMETHOD_DEFAULT,D3DDECLUSAGE_BINORMAL,0},

		//end
		D3DDECL_END(),
	};

	D3DVERTEXELEMENT9 quadFormat[] =
	{
		{0,0,D3DDECLTYPE_FLOAT3,D3DDECLMETHOD_DEFAULT,D3DDECLUSAGE_POSITIONT,0},
		{0,16,D3DDECLTYPE_FLOAT2,D3DDECLMETHOD_DEFAULT,D3DDECLUSAGE_TEXCOORD,0},
		{0,24,D3DDECLTYPE_FLOAT2,D3DDECLMETHOD_DEFAULT,D3DDECLUSAGE_TEXCOORD,1},

		//end
		D3DDECL_END(),
	};

	//set the vertex delcaration to declared format
	m_device->CreateVertexDeclaration(vertFormat,&m_vertDeclMesh);
	m_device->CreateVertexDeclaration(quadFormat,&m_vertDeclQuad);

	//create screen & sprite quad
	float o = -0.5f;
	float w = (float)m_window->m_windowWidth;
	float h = (float)m_window->m_windowHeight;
	D3DFQuad quad[4] =
	{
		{ o,   o,   1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f },
		{ w+o, o,   1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f },
		{ o,   h+o, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f },
		{ w+o, h+o, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f },
	};

	m_screenQuad[0] = quad[0];
	m_screenQuad[1] = quad[1];
	m_screenQuad[2] = quad[2];
	m_screenQuad[3] = quad[3];

	m_spriteQuad[0] = quad[0];
	m_spriteQuad[1] = quad[1];
	m_spriteQuad[2] = quad[2];
	m_spriteQuad[3] = quad[3];
	m_spriteQuad[0].z = 0.0f;
	m_spriteQuad[1].z = 0.0f;
	m_spriteQuad[2].z = 0.0f;
	m_spriteQuad[3].z = 0.0f;

	//set camera attributes
	m_camAspect = ((float)m_window->m_windowWidth)/((float)m_window->m_windowHeight);
	m_camNear   = 1.0f;
	m_camFar    = 2048.0f;
	m_camFov    = D3DX_PI/4.0f;

	//return after completion
	return D3D_SUCCESS;
}

int Direct3DFramework::release()
{
	//perform releases
	for(int i = 0; i < m_effectList.size(); i++)
	{
		D3DF_RELEASE_URN(m_effectList[i]->m_effect);
		delete m_effectList[i];
	}

	for(int i = 0; i < m_meshList.size(); i++)
	{
		D3DF_RELEASE_URN(m_meshList[i]->m_mesh);
		delete m_meshList[i];
	}

	for(int i = 0; i < m_texture2DList.size(); i++)
	{
		D3DF_RELEASE_URN(m_texture2DList[i]->m_texture);
		delete m_texture2DList[i];
	}

	for(int i = 0; i < m_texture3DList.size(); i++)
	{
		D3DF_RELEASE_URN(m_texture3DList[i]->m_texture);
		delete m_texture2DList[i];
	}

	for(int i = 0; i < m_surfaceList.size(); i++)
	{
		D3DF_RELEASE_URN(m_surfaceList[i]->m_surface);
		delete m_surfaceList[i];
	}

	m_effectList.release();
	m_meshList.release();
	m_texture2DList.release();
	m_texture3DList.release();
	m_surfaceList.release();

	D3DF_RELEASE_URN(m_object);
	D3DF_RELEASE_URN(m_device);

	//return after completion
	return D3D_SUCCESS;
}

int Direct3DFramework::setEffect(DEffect* a_effect)
{
	//check if given handle is valid
	if(a_effect == 0) return D3D_SUCCESS;

	//check if effect list is empty
	if(m_effectList.size() == 0) return D3D_SUCCESS;

	//set the current effect to the given handle
	m_cEffect = m_effectList[a_effect->m_effId];

	//return after completion
	return D3D_SUCCESS;
}

int Direct3DFramework::setTechnique(char* a_technique)
{
	//check if given handle is valid
	if(a_technique == 0) return D3D_SUCCESS;

	//set the current technique to the given handle
	m_cEffect->m_technique = m_cEffect->m_effect->GetTechniqueByName(a_technique);

	//return after completion
	return D3D_SUCCESS;
}

int Direct3DFramework::setMaterial(DMaterial* a_material)
{
	//check if given handle is valid
	if(a_material == 0) return D3D_SUCCESS;

	//set the current material to the given handle
	m_cMaterial = *a_material;

	//return after completion
	return D3D_SUCCESS;
}

int Direct3DFramework::setLight(DLight* a_light)
{
	//check if given handle is valid
	if(a_light == 0) return D3D_SUCCESS;

	//set the current light to the given handle
	m_cLight = *a_light;

	//return after completion
	return D3D_SUCCESS;
}

int Direct3DFramework::setTexture2D(DTexture2D* a_texture, int a_number)
{
	//check if given handle is valid
	if(a_texture == 0) return D3D_SUCCESS;

	//check if texture list is empty
	if(m_texture2DList.size() == 0) return D3D_SUCCESS;

	//set the current texture to the given handle
	switch(a_number)
	{
	case 0:
		m_cTexture2D[0] = m_texture2DList[a_texture->m_texId]->m_texture;
		break;
	case 1:
		m_cTexture2D[1] = m_texture2DList[a_texture->m_texId]->m_texture;
		break;
	case 2:
		m_cTexture2D[2] = m_texture2DList[a_texture->m_texId]->m_texture;
		break;
	case 3:
		m_cTexture2D[3] = m_texture2DList[a_texture->m_texId]->m_texture;
		break;
	case 4:
		m_cTexture2D[4] = m_texture2DList[a_texture->m_texId]->m_texture;
		break;
	case 5:
		m_cTexture2D[5] = m_texture2DList[a_texture->m_texId]->m_texture;
		break;
	}

	//return after completion
	return D3D_SUCCESS;
}

int Direct3DFramework::setTexture3D(DTexture3D* a_texture, int a_number)
{
	//check if given handle is valid
	if(a_texture == 0) return D3D_SUCCESS;

	//check if texture list is empty
	if(m_texture2DList.size() == 0) return D3D_SUCCESS;

	//set the current texture to the given handle
	switch(a_number)
	{
	case 0:
		m_cTexture3D[0] = m_texture3DList[a_texture->m_texId]->m_texture;
		break;
	case 1:
		m_cTexture3D[1] = m_texture3DList[a_texture->m_texId]->m_texture;
		break;
	case 2:
		m_cTexture3D[2] = m_texture3DList[a_texture->m_texId]->m_texture;
		break;
	case 3:
		m_cTexture3D[3] = m_texture3DList[a_texture->m_texId]->m_texture;
		break;
	case 4:
		m_cTexture3D[4] = m_texture3DList[a_texture->m_texId]->m_texture;
		break;
	case 5:
		m_cTexture3D[5] = m_texture3DList[a_texture->m_texId]->m_texture;
		break;
	}

	//return after completion
	return D3D_SUCCESS;
}